import Layers from "@/lang/layers";
import axios from 'axios'
import application from '../starter/application-context'
import Utils from "./core/utils";
import Dialog from "./layers/dialog";
import StringUtils from "./commons/string-utils";
import StringBuilder from "./core/string-builder";

/**
 * 请求上下文路径
 */
const context_path: string = 'http://127.0.0.1:7081/sea';

/**
 * 请求上下文
 */
const contextPath = () => {
    return context_path;
}

/**
 * axios 实例
 *
 * 全局 http 总控
 *
 * @type {axios.AxiosInstance}
 */
const executor = axios.create({
    // 请求URL公共部分
    baseURL: context_path,
    // 超时时间
    timeout: 10000
})

/**
 * 请求拦截
 */
executor.interceptors.request.use(config => {
    //开启加载条
    // if (config.loading === true) {
    //     Layers.startLoading();
    // }
    return config;
}, error => {
    return Promise.reject(error)
});

/**
 * 响应拦截
 */
executor.interceptors.response.use(response => {
    // 其它类型的响应数据
    return response.data;
}, error => {
    // 无响应
    if (error.response === undefined) {
        Layers.warn('后台服务未响应！');
        return Promise.reject(error);
    }

    const response = error.response;

    // 4xx 和 5xx 都可能存在 problem+json，这里简单的处理一下
    if (response.status === 400) {
        // 请求参数未通过校验
        Layers.warn('表单参数未通过校验！');
        const data = response.data;
        return Promise.reject(data);
    } else if (response.status === 402 || response.status === 403) {
        // 402 未登录或者 refresh token 过时， 403 账号在其他地方登录
        application.logout();
        Layers.warn('登录状态异常！');
    } else if (response.status === 504) {
        // 网关超时 gateway timeout
        Layers.warn(response.data || response || error);
    } else {
        // 通用 500 错误
        if (response.status === 500) {
            const data = response.data;
            if (typeof data === 'string') {
                Layers.warn('操作失败:' + data);
            } else {
                let msg = data.data;
                if (typeof data === 'string') {
                    Layers.warn('操作失败:' + msg);
                } else {
                    Layers.warn('操作失败!');
                }
            }
            return Promise.reject(data);
        }
    }
    return Promise.reject(error);
});

/**
 * 格式化参数
 *
 * 当前函数，对请求参数，进行了最基本的格式转换：
 *
 * 将所有参数转换成字符串，并去除所有空字符串。
 *
 * @param params - 请求参数
 * @returns 序列化之后数据 processRequestParams
 */
const processRequestParams = (params: Record<string, unknown>): Record<string, string> => {
    let ret = {};
    for (let key in params) {
        if (params.hasOwnProperty(key)) {
            let val = params[key];

            if (val == null || val === '') {
                // do nothing
                // 空字符串会取消提交，需要注意这一点
            } else if (typeof val === 'string') {
                // 字符串，自动去除前后空格
                val = val.trim();
                if (val.length > 0) {
                    ret[key] = val;
                }
            } else if (Array.isArray(val)) {
                // 数组用逗号拼接
                ret[key] = val.join(',');
            } else if (Date.prototype.isPrototypeOf(val)) {
                // 日期默认转 datetime 类型
                ret[key] = StringUtils.datetime(val);
            } else {
                // 其它类型转换成字符串
                ret[key] = val.toString();
            }
        }
    }
    return ret;
}

/**
 * ajax封装（日常业务使用的 http）
 *
 * 对项目中的所有请求进行二次封装，做了一层总的代理，内容如下：
 *
 * - 1、数据提交之前，去除了前后空格；
 * - 2、很明显的异常错误，有默认的弹窗提示；
 * - 3、DELETE请求，默认给确认窗口，需要用户确认后提交。
 */
const ajax = (request: ApiRequest): Promise<unknown> => {
    let params: Record<string, unknown> = request.params;

    if (Utils.isNotEmpty(params)) {
        request.params = processRequestParams(params);
    }

    let data = request.data;
    if (Utils.isNotEmpty(data) && !(data instanceof FormData)) {
        request.data = processRequestParams(data);
    }

    return executor(request);
};

/**
 * 发送POST请求
 *
 * @param request - ajax配置
 * @returns 后台请求响应
 */
const post = (request: ApiRequest): Promise<unknown> => {
    request.method = 'POST';
    return ajax(request);
};

/**
 * 发送GET请求
 *
 * @param request - ajax配置
 * @returns 后台请求响应
 */
const get = (request: ApiRequest): Promise<unknown> => {
        request.method = 'GET';
        return ajax(request);
    }
;

/**
 * 发送DELETE请求，自动给出弹窗提示
 *
 * @param request - ajax配置
 * @returns 后台请求响应
 */
const delete_ = (request: ApiRequest): Promise<unknown> => {
        request.method = 'DELETE';
        return Dialog.confirm('是否确定删除？').then(() => {
            return ajax(request)
        });
    }
;


/**
 * 发生PUT请求
 *
 * @param request - ajax配置
 * @returns 后台请求响应
 */
const put = (request: ApiRequest): Promise<unknown> => {
    request.method = 'PUT';
    return ajax(request);
}


/**
 * 格式化参数
 *
 * 当前函数，对请求参数，进行了最基本的格式转换：
 *
 * 将所有参数转换成字符串，并去除所有空字符串。
 *
 * @param params - 请求参数
 * @returns 序列化之后数据 processRequestParams
 */
const toFormData = (params: Record<string, unknown>): FormData => {
    const ret = new FormData();
    Object.keys(params).forEach(key => ret.append(key, params[key]));
    return ret;
}


/**
 * 链式 http
 *
 * 顺序执行多个 http，只要中间有任何异常抛出，则直接抛到最后面的异常catch块，
 * 代码写法参考下列代码注释
 *
 * <code><pre>
 * linkedAjax()
 *      .then(action => {
 *          return action('test1').then(str => {
 *              console.log(str);
 *              return action;
 *          });
 *      }).then(action => {
 *          return action('test1').then(str => {
 *              console.log(str);
 *              return action;
 *          });
 *      }).catch(e=>{
 *           console.err(e);
 *      });
 * </pre></code>
 * @returns {Promise}
 */
const linkedAjax = () => {
    return Promise.resolve(http)
}

/**
 * 并行执行多个 http 请求
 *
 * @param requests - http 配置
 * @returns 各个请求响应组成的数组
 */
const multipleAjax = (requests: Request[]): Promise<[unknown]> => {
        let all = [];
        for (let i = 0; i < requests.length; i++) {
            all.push(
                new Promise((resolve, reject) => {
                    http(requests[i]).then(res => {
                        resolve(res);
                    }).catch((err) => {
                        reject(err);
                    });
                })
            )
        }
        return Promise.all(all);
    }
;

/**
 * 将参数添加到请求路径中
 *
 * @param url - 地址
 * @param params - 请求参数
 * @returns GET 请求适用的 url
 */
const addParamsToUrl = (url: string, params: Record<string, unknown>) => {
    if (params === undefined) {
        return url;
    } else {
        const sb = new StringBuilder();
        sb.append(url);
        let idx = url.indexOf('?');
        if (idx < 0) {
            sb.append('?')
        } else if (idx !== url.length - 1) {
            if (url.charAt(url.length - 1) !== '&') {
                sb.append('&')
            }
        }

        const args = processRequestParams(params);
        for (let key in args) {
            if (args.hasOwnProperty(key)) {
                let v = args[key];
                if (Utils.isNotEmpty(v)) {
                    sb.append(key).append('=').append(encodeURIComponent(args[key])).append('&');
                }
            }
        }

        // 如果有强迫症，可以在这里去除最后一个 & 符号

        return sb.build();
    }
}

/**
 * 获取 url 中的参数，经过 url 解码
 *
 * @param url - 地址
 * @returns 获取 url 中的请求参数
 */
const getParamsOfUrl = (url: string): Record<string, string> => {
    let res = {};
    let idx = url.lastIndexOf('?');
    if (idx > 0) {
        let p = url.substring(idx + 1, url.length);

        // 兼容这种格式：http://www.baidu.com?name=min&age=16#eg1#eg2
        let idx = p.indexOf('#');
        if (idx > 0) {
            p = p.substring(0, idx);
        }

        let arr = p.split('&');
        for (let i = 0; i < arr.length; i++) {
            let a = arr[i].split('=');
            if (a.length > 1) {
                res[a[0]] = decodeURI(a[1])
            }
        }
    }
    return res;
}

/**
 * 一些全局的设计接口设计
 */

/**
 * http 请求方式
 */
export type ApiRequestMethod =
    | 'get' | 'GET'
    | 'delete' | 'DELETE'
    | 'head' | 'HEAD'
    | 'options' | 'OPTIONS'
    | 'post' | 'POST'
    | 'put' | 'PUT'
    | 'patch' | 'PATCH'
    | 'purge' | 'PURGE'
    | 'link' | 'LINK'
    | 'unlink' | 'UNLINK';

/**
 * 简单的查询条件
 *
 * 并不是标准接口，按需调整
 */
export interface ApiRequest<D = any> {
    url?: string; // 请求地址
    data?: D; // request-body | FormData，需要设置对应的 content-type
    params?: Record<string, unknown>; // 请求参数
    method?: ApiRequestMethod | string; // 请求方式
    headers?: Record<string, string | number>; // 请求头
    timeout?: number; // 请求超时
}


/**
 * 数据响应 - 更新
 *
 * 因为大部分更新操作，只要响应 200 状态，即表示成功，所以当前操作实际不怎么被使用
 */
export interface ApiResponse<D = any> {
    /**
     * 结果码
     *
     * http-status = 200 表示本次业务成功，当前码值用于更细致的业务状态划分；
     * 通常情况下 0 表示正常，非 0 则需要业务上给出解释；
     */
    code?: number;
    /**
     * 响应数据
     */
    data?: D;
}

/**
 * 数据响应 - 分页
 */
export interface ApiResponsePage<D = any> {
    // 数据列表
    list: Array<D>;
    // 数据总量
    total: number;
}

export default {
    executor, ajax, get, post, 'delete': delete_, put,

    multipleAjax, linkedAjax,

    processRequestParams, toFormData,

    addParamsToUrl, getParamsOfUrl, contextPath
}